import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.time.Duration;
import java.time.LocalDateTime;
import java.util.Date;
import java.util.Locale;
import java.util.TimeZone;

enum TimeStampScale {
    Second(0),
    Ms(1),
    Us(2),
    Ns(3);

    private int value;
    TimeStampScale(int i) {
        this.value = i;
    }
}

public class DateTimeDemo {

    // 时间戳\日期互转
    protected static void timeStampDemo() {
        Date date = new Date();
        long times = date.getTime();

        System.out.println("当前时间:"+date+" --> 毫秒时间戳:" + times);

        Date date2 = new Date(times);
        System.out.println("当前时间:"+date2+" <-- 毫秒时间戳:" + times);
    }

    // 字符串\日期互转
    protected static void timeStringDemo() {
/*
https://www.cnblogs.com/HOI-Yzy/p/14671806.html
转换符   说明                        示例
c       包括全部日期和时间信息         星期六 十月 27 14:21:20 CST 2007
F       “年-月-日”格式               2007-10-27
D       “月/日/年”格式               10/27/07
r       “HH:MM:SS PM”格式（12时制）   02:25:51 下午
T       “HH:MM:SS”格式（24时制）      14:28:16
R       “HH:MM”格式（24时制）         14:28


转换符  说明                               示例
H   2位数字24时制的小时（不足2位前面补0）       15
I   2位数字12时制的小时（不足2位前面补0）       03
k   2位数字24时制的小时（前面不补0）           15
l   2位数字12时制的小时（前面不补0）           3
M   2位数字的分钟（不足2位前面补0）            03
S   2位数字的秒（不足2位前面补0）              09
L   3位数字的毫秒（不足3位前面补0）            015
N   9位数字的毫秒数（不足9位前面补0）           562000000
p   小写字母的上午或下午标记                   中：下午 英：pm
z   相对于GMT的RFC822时区的偏移量             +0800
Z   时区缩写字符串                           CST
s   1970-1-1 00:00:00 到现在所经过的秒数      1193468128
Q   1970-1-1 00:00:00 到现在所经过的毫秒数    1193468128984

*/
        Date date = new Date();
        //c的使用
        System.out.printf("全部日期和时间信息：        %tc%n", date);
        //f的使用
        System.out.printf("年-月-日格式：             %tF%n", date);
        //d的使用
        System.out.printf("月/日/年格式：             %tD%n", date);
        //r的使用
        System.out.printf("HH:MM:SS PM格式（12时制）：%tr%n", date);
        //t的使用
        System.out.printf("HH:MM:SS格式（24时制）：   %tT%n", date);
        //R的使用
        System.out.printf("HH:MM格式（24时制）：      %tR", date);


        //H的使用
        System.out.printf("2位数字24时制的小时（不足2位前面补0）:%tH%n", date);
        //I的使用
        System.out.printf("2位数字12时制的小时（不足2位前面补0）:%tI%n", date);
        //k的使用
        System.out.printf("2位数字24时制的小时（前面不补0）:%tk%n", date);
        //l的使用
        System.out.printf("2位数字12时制的小时（前面不补0）:%tl%n", date);
        //M的使用
        System.out.printf("2位数字的分钟（不足2位前面补0）:%tM%n", date);
        //S的使用
        System.out.printf("2位数字的秒（不足2位前面补0）:%tS%n", date);
        //L的使用
        System.out.printf("3位数字的毫秒（不足3位前面补0）:%tL%n", date);
        //N的使用
        System.out.printf("9位数字的毫秒数（不足9位前面补0）:%tN%n", date);
        //p的使用
        String str = String.format(Locale.US, "小写字母的上午或下午标记(英)：%tp", date);
        System.out.println(str);
        System.out.printf(Locale.CHINA,"小写字母的上午或下午标记（中）：%tp%n", date);
        //z的使用
        System.out.printf("相对于GMT的RFC822时区的偏移量:%tz%n", date);
        //Z的使用
        System.out.printf("时区缩写字符串:%tZ%n", date);
        //s的使用
        System.out.printf("1970-1-1 00:00:00 到现在所经过的秒数：%ts%n", date);
        //Q的使用
        System.out.printf("1970-1-1 00:00:00 到现在所经过的毫秒数：%tQ%n", date);

        System.out.printf("使用$和时间格式符号：%1$tH:%1$tM:%1$tS.%1$tL%n", date);

        /**
         SimpleDateFormat函数语法：

         G 年代标志符
         y 年
         M 月
         d 日
         h 时 在上午或下午 (1~12)
         H 时 在一天中 (0~23)
         m 分
         s 秒
         S 毫秒
         E 星期
         D 一年中的第几天
         F 一月中第几个星期几
         w 一年中第几个星期
         W 一月中第几个星期
         a 上午 / 下午 标记符
         k 时 在一天中 (1~24)
         K 时 在上午或下午 (0~11)
         z 时区
         */

        SimpleDateFormat myFmt=new SimpleDateFormat("z yyyy年MM月dd日 HH时mm分ss秒");
        SimpleDateFormat myFmt1=new SimpleDateFormat("z yy/MM/dd HH:mm");
        SimpleDateFormat myFmt2=new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");//等价于now.toLocaleString()
        SimpleDateFormat myFmt3=new SimpleDateFormat("z yyyy年MM月dd日 HH时mm分ss秒SS'ms' E "); // 单引号中的部分原样输出
        SimpleDateFormat myFmt4=new SimpleDateFormat(
                "z时区 一年中的第D天 一年中第w个星期 一月中第W个星期 在一天中k时 yyyy-MM-dd HH:mm:ss");

        System.out.println("使用SimpleDateFormat "+myFmt.format(date));
        System.out.println("使用SimpleDateFormat "+myFmt1.format(date));
        String dateStr = myFmt2.format(date);
        System.out.println("使用 yyyy-MM-dd HH:mm:ss "+dateStr);
        System.out.println("使用SimpleDateFormat "+myFmt3.format(date));
        System.out.println("使用SimpleDateFormat "+myFmt4.format(date));

        // 时间日期字符串 转换为 Date 对象
        Date dateFromStr=null;
        try {
            dateFromStr = myFmt2.parse(dateStr);
            System.out.println("dateFromStr "+myFmt4.format(dateFromStr));
        } catch (ParseException e) {
            throw new RuntimeException(e);
        }


        // SimpleDateFormat 调整时区后，时间日期字符串 转换为 Date 对象
        System.out.println("SimpleDateFormat TimeZone "+myFmt2.getTimeZone());
        myFmt2.setTimeZone(TimeZone.getTimeZone("GMT+0:00"));
        System.out.println("SimpleDateFormat TimeZone "+myFmt2.getTimeZone());
        try {
            dateFromStr = myFmt2.parse(dateStr);
            System.out.println("dateFromStr 调整时区后，format和parse 时区一致 "+myFmt2.format(dateFromStr));
            System.out.println("dateFromStr 调整时区后，format和parse 时区不一致 "+myFmt1.format(dateFromStr));
        } catch (ParseException e) {
            throw new RuntimeException(e);
        }
        //dateFromStr.time
    }

    // 返回某一天(当前时区) 0点 毫秒时间戳
    protected static long firstTimeStampOfADay(Date theDay) {
        SimpleDateFormat myFmt=new SimpleDateFormat("yyyy-MM-dd");//等价于now.toLocaleString()
        try {
            Date dateFromStr=myFmt.parse(myFmt.format(theDay));
            return dateFromStr.getTime();
        } catch (ParseException e) {
            return 0l;
        }
    }

    // 返回今天(当前时区) 0点 毫秒时间戳
    protected static long firstTimeStampOfToday() {
        return firstTimeStampOfADay(new Date());
    }

    protected static void firstTSDemo() {
        long todayFirstTS = firstTimeStampOfToday();
        System.out.println("今天0点的毫秒时间戳 "+todayFirstTS);
        long yesterdayFirstTS = firstTimeStampOfADay(new Date(todayFirstTS-1));
        System.out.println("昨天天0点的毫秒时间戳 "+yesterdayFirstTS);
    }

    private static LocalDateTime logtsBase;

    protected static void resetTsBase() {
        logtsBase = LocalDateTime.now();
    }
    protected static void logTsBegin(String info) {
        logtsBase = LocalDateTime.now();
        System.out.println("\n0ws, "+info);
    }
    protected static void logTs(String info) {
        //LocalDateTime nowTime = LocalDateTime.now();
        Duration duration = Duration.between(logtsBase, LocalDateTime.now());
        String tn = Thread.currentThread().getName();
        //System.out.println("\n"+(duration.toNanos()/1000) + "ws, "+info);
        System.out.println("\n"+(duration.toMillis()) + "ms, "+tn+", "+info);
    }

    protected static void logTs(String info, TimeStampScale durationScale, boolean showThread) {
        Duration duration = Duration.between(logtsBase, LocalDateTime.now());

        String tn = showThread ? Thread.currentThread().getName() : "";
        String durationStr = "";
        switch (durationScale) {
            case Second :
                durationStr = duration.toSeconds() + "s";
                break;
            case Ms:
                durationStr = duration.toMillis() + "ms";
                break;
            case Us:
                durationStr = (duration.toNanos()/1000) + "us";
                break;
            case Ns:
                durationStr = duration.toNanos() + "ns";
                break;
        }

        System.out.println("\n"+durationStr+","+tn+(tn.length()>0 ? ", ":"")+info);
    }

    LocalDateTime localDateTime=LocalDateTime.now();

    protected static void demoLogic() {
        // 时间戳\日期互转
        DateTimeDemo.logTs("timeStampDemo");
        timeStampDemo();

        // 字符串\日期互转
        DateTimeDemo.logTs("timeStringDemo");
        timeStringDemo();

        //
        DateTimeDemo.logTs("firstTSDemo");
        firstTSDemo();
    }
}
